/**
 * @file TS.c
 *
 * @author Luc Fourestier
 * @date 2009
 */

#include "TS.h"

#include "Global.h"

#define SYNC_BYTE 0x47


/**
 * Create the TS_t structure
 *
 * @param Buffer: The incoming buffer containing the TS
 * @param BuffSize: in/out - the max buffer size and the actual parsed size as output
 * @param TS: The TS output pointer (must be allocated by caller)
 * @return
 */
int TS_Create(unsigned char * Buffer, unsigned int *Size, TS_t * TS)
{
#define step(n) Index += n

	int RetVal = OK;
	unsigned int Index = 0;

	if((TS != NULL) && (Buffer != NULL) && (Size != NULL) && (*Size >= TS_SIZE))
	{
		if (Buffer[0] == SYNC_BYTE)
		{
			TS->sync_byte = GET_U8(Buffer, Index);
			step(1);
			TS->transport_error_indicator = (GET_U8(Buffer, Index) & 0x80) >> 7;
			TS->payload_unit_start_indicator = (GET_U8(Buffer, Index) & 0x40) >> 6;
			TS->transport_priority = (GET_U8(Buffer, Index) & 0x20) >> 5;
			TS->PID = GET_U16(Buffer, Index) & 0x1fff;
			step(2);
			TS->transport_scrambling_control = (GET_U8(Buffer, Index) & 0xc0) >> 6;
			TS->adaptation_field_control = (GET_U8(Buffer, Index) & 0x30) >> 4;
			TS->continuity_counter = GET_U8(Buffer, Index) & 0xf;
			step(1);

			// Adaptation field
			if (TS->adaptation_field_control & 0x2)
			{
				TS->adaptation_field.adaptation_field_length = GET_U8(Buffer, Index);
				step(1);

				if (TS->adaptation_field.adaptation_field_length > 0)
				{
					unsigned int AdaptFieldIdx = Index;

					TS->adaptation_field.discontinuity_indicator = (GET_U8(Buffer, Index) & 0x80) >> 7;
					TS->adaptation_field.random_access_indicator = (GET_U8(Buffer, Index) & 0x40) >> 6;
					TS->adaptation_field.elementary_stream_priority_indicator = (GET_U8(Buffer, Index) & 0x20) >> 5;
					TS->adaptation_field.PCR_flag = (GET_U8(Buffer, Index) & 0x10) >> 4;
					TS->adaptation_field.OPCR_flag = (GET_U8(Buffer, Index) & 0x8) >> 3;
					TS->adaptation_field.splicing_point_flag = (GET_U8(Buffer, Index) & 0x4) >> 2;
					TS->adaptation_field.transport_private_data_flag = (GET_U8(Buffer, Index) & 0x2) >> 1;
					TS->adaptation_field.adaptation_field_extension_flag = GET_U8(Buffer, Index) & 0x1;
					step(1);

					if (TS->adaptation_field.PCR_flag)
					{
						TS->adaptation_field.program_clock_reference_base = (GET_U64(Buffer, Index) & 0xffffffff80000000LL) >> 31;
						step(4);
						TS->adaptation_field.program_clock_reference_extension = GET_U16(Buffer, Index) & 0x1ff;
						step(2);
					}

					if (TS->adaptation_field.OPCR_flag)
					{
						TS->adaptation_field.original_program_clock_reference_base = (GET_U64(Buffer, Index) & 0xffffffff80000000LL) >> 31;
						step(4);
						TS->adaptation_field.original_program_clock_reference_extension = GET_U16(Buffer, Index) & 0x1ff;
						step(2);
					}

					if (TS->adaptation_field.splicing_point_flag)
					{
						TS->adaptation_field.splice_countdown = GET_U8(Buffer, Index);
						step(1);
					}

					if (TS->adaptation_field.transport_private_data_flag)
					{
						unsigned int PrivateDataIdx;

						TS->adaptation_field.transport_private_data_length = GET_U8(Buffer, Index);
						step(1);

						PrivateDataIdx = 0;
						while((Index < (*Size)) && (PrivateDataIdx < TS->adaptation_field.transport_private_data_length))
						{
							TS->adaptation_field.private_data_byte[PrivateDataIdx] = GET_U8(Buffer, Index);
							step(1);
							PrivateDataIdx++;
						}
					}

					if (TS->adaptation_field.adaptation_field_extension_flag)
					{
						unsigned int AdaptFieldExtIdx;

						TS->adaptation_field.adaptation_field_extension_length = GET_U8(Buffer, Index);
						step(1);
						AdaptFieldExtIdx = Index;

						//! @todo add other field here

						while((Index < (*Size)) &&
							  (Index < (AdaptFieldExtIdx + TS->adaptation_field.adaptation_field_extension_length)))
						{
							step(1);
						}
					}

					// Stuffing byte
					while((Index < (*Size)) &&
						  (Index < (AdaptFieldIdx + TS->adaptation_field.adaptation_field_length)))
					{
						step(1);
					}
				}
			}

			// data byte
			if (TS->adaptation_field_control & 0x1)
			{
				unsigned int Temp = 0;
				while((Index < (*Size)) && (Index < TS_SIZE))
				{
					TS->data_byte[Temp] = GET_U8(Buffer, Index);
					step(1);
					Temp++;
				}
				TS->DataByteLength = Temp;

			}

			*Size = Index;
		}
		else
		{
			RetVal = TS_ERROR_NOT_A_TS;
		}
	}
	else
	{
		RetVal = ERROR;
	}

	return(RetVal);

#undef step
}

/**
 * A NULL TS packet that can be used to pad a stream.
 */
unsigned char NullPacket[] =
{
	0x47, 0x1f, 0xff, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};


